#include <QTimer>
#include <QUdpSocket>
#include <QStringList>
#include "eventop.h"
#include "macro.h"

EventOP::EventOP(QUdpSocket *paramUdpSocket, QObject *parent) :
    QObject(parent)
{
    udpSocket=paramUdpSocket;
    timer=new QTimer(this);
    timer->start(60000); //1 minute
    connect(timer,SIGNAL(timeout()),this,SLOT(timerEventForTTL()));
}

void EventOP::processEvent(QByteArray dataGram, QString strIP, quint16 port)
{
    emit sigInfo("event occured: "+dataGram);
    dataGram=dataGram.simplified().toUpper();    

    if(dataGram.startsWith("EVENT WATCH"))
    {
        QList <qint64> listNodeAddr;
        QList <QByteArray> strlistNodeAddr=dataGram.split(' ');
        strlistNodeAddr.removeFirst();
        strlistNodeAddr.removeFirst();
        while(!strlistNodeAddr.isEmpty())
        {
            bool ok;
            qint64 addr=strlistNodeAddr.at(0).toLongLong(&ok,10);
            if(!ok)
                break;
            else
            {
                listNodeAddr.append(addr);
                strlistNodeAddr.removeFirst();
            }
        }
        doWithEventWatch(strIP+":"+QByteArray::number(port,10),listNodeAddr);
    }
    else if(dataGram.startsWith("EVENT DISWATCH"))
    {
        QList <qint64> listNodeAddr;
        QList <QByteArray> strlistNodeAddr=dataGram.split(' ');
        strlistNodeAddr.removeFirst();
        strlistNodeAddr.removeFirst();
        while(!strlistNodeAddr.isEmpty())
        {
            bool ok;
            qint64 addr=strlistNodeAddr.at(0).toLongLong(&ok,10);
            if(!ok)
                break;
            else
            {
                listNodeAddr.append(addr);
                strlistNodeAddr.removeFirst();
            }
        }
        doWithEventDisWatch(strIP+":"+QByteArray::number(port,10),listNodeAddr);
    }
    else if(dataGram.startsWith("EVENT ALIVE"))
    {
        QList <qint64> listNodeAddr;
        QList <QByteArray> strlistNodeAddr=dataGram.split(' ');
        strlistNodeAddr.removeFirst();
        strlistNodeAddr.removeFirst();
        while(!strlistNodeAddr.isEmpty())
        {
            bool ok;
            qint64 addr=strlistNodeAddr.at(0).toLongLong(&ok,10);
            if(!ok)
                break;
            else
            {
                listNodeAddr.append(addr);
                strlistNodeAddr.removeFirst();
            }
        }
        doWithEventAlive(strIP+":"+QByteArray::number(port,10),listNodeAddr);
    }
    else if(dataGram.startsWith("EVENT NOTIFY"))
    {
        doWithEventNotify(strIP+":"+QByteArray::number(port,10),dataGram);
    }
}

void EventOP::insertAddrMacsMultiMap(QString IPaddr, qint64 nodeAddr)
{
    QByteArray reply;
    if(!socketMacsMultiMap.contains(IPaddr,nodeAddr))
    {
        socketMacsMultiMap.insert(IPaddr,nodeAddr);
        socketTTLMap.insert(IPaddr,EVENT_TTL_MAX); //相当于作为心跳处理
        reply="INFO: Begin to Watch "+QByteArray::number(nodeAddr,10);
        QHostAddress host=QHostAddress(IPaddr.split(":").at(0));
        quint16 port=IPaddr.split(":").at(1).toUShort();
        if(!host.toString().isEmpty()&&host.toString().count(".")==3)
            udpSocket->writeDatagram(reply,host,port);
    }

}

void EventOP::removeAddrMacsMultiMap(QString IPaddr, qint64 nodeAddr)
{
    QByteArray reply;
    socketMacsMultiMap.remove(IPaddr,nodeAddr);
    if(!socketMacsMultiMap.contains(IPaddr))
        socketTTLMap.remove(IPaddr);
    reply="INFO: Stop Watching "+QByteArray::number(nodeAddr,10);
    QHostAddress host=QHostAddress(IPaddr.split(":").at(0));
    quint16 port=IPaddr.split(":").at(1).toUShort();
    if(!host.toString().isEmpty()&&host.toString().count(".")==3)
        udpSocket->writeDatagram(reply,host,port);
}

void EventOP::doWithExpiredSocket(QString IPaddr)
{
    socketTTLMap.remove(IPaddr);
    socketMacsMultiMap.remove(IPaddr);
    QByteArray reply="INFO: Stop Watching Nodes";
    QHostAddress host=QHostAddress(IPaddr.split(":").at(0));
    quint16 port=IPaddr.split(":").at(1).toUShort();
    if(!host.toString().isEmpty()&&host.toString().count(".")==3)
        udpSocket->writeDatagram(reply,host,port);
}

void EventOP::doWithEventWatch(QString IPaddr, QList<qint64> listNodeAddr)
{
    int nodeNum=listNodeAddr.size();
    for(int n=0;n<nodeNum;n++)
    {
        insertAddrMacsMultiMap(IPaddr,listNodeAddr.at(n));
    }
}

void EventOP::doWithEventDisWatch(QString IPaddr, QList<qint64> listNodeAddr)
{
    int nodeNum=listNodeAddr.size();
    for(int n=0;n<nodeNum;n++)
    {
        removeAddrMacsMultiMap(IPaddr,listNodeAddr.at(n));
    }
}

void EventOP::doWithEventAlive(QString IPaddr, QList<qint64> listNodeAddr)
{
    if(socketTTLMap.contains(IPaddr)) //连接未断
        updateSocketTTLMap(IPaddr,EVENT_TTL_MAX);
    else //连接已断
    {
        doWithEventWatch(IPaddr,listNodeAddr);
    }
}

void EventOP::doWithEventNotify(QString IPaddr, QByteArray dataGram)
{
    QHostAddress host=QHostAddress(IPaddr.split(":").at(0));
    quint16 port=IPaddr.split(":").at(1).toUInt();
    bool ok;
    qint64 nodeAddr=dataGram.split(' ').at(2).toLongLong(&ok,10);
    if(!ok)
    {
        if(!host.toString().isEmpty()&&host.toString().count(".")==3)
         udpSocket->writeDatagram("ERROR: MacAddressed not recognized. Make sure that the "
            "packet is like \"Event Notify Event Notify 5149012977665754 SensorData DoorMagnet 1\"",host,port);
        return; //地址转换失败，则结束
    }

    dataGram=dataGram.replace("NOTIFY",""); //去除这个单词

    QStringList listSockets=socketMacsMultiMap.keys(nodeAddr);
    int socketNum=listSockets.size();
    QString strIPPort;
    for(int n=0;n<socketNum;n++)
    {
        strIPPort=listSockets.at(n);
        host=QHostAddress(strIPPort.split(":").at(0));
        port=strIPPort.split(":").at(1).toUShort();
        if(!host.toString().isEmpty()&&host.toString().count(".")==3)
        {
            udpSocket->writeDatagram(dataGram,host,port);
            emit sigInfo(dataGram+" is sent to "+host.toString()+":"+QString::number(port));
        }
    }

}

void EventOP::timerEventForTTL()
{
    QMapIterator<QString,quint8> i(socketTTLMap);  //遍历整个map
    while (i.hasNext())
    {
        i.next();
        if(i.value()<=0) //该TTL过期
        {
            doWithExpiredSocket(i.key()); //作相应处理
        }
        else
            updateSocketTTLMap(i.key(),i.value()-1);
    }
}

void EventOP::updateSocketTTLMap(QString IPaddr, quint8 newValue)
{
    socketTTLMap.insert(IPaddr,newValue);
}

